#include <Base.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/IoLib.h>
#include <Library/BaseLib.h>
#include <Library/PeiServicesLib.h>

EFI_GUID gEfiCmosManagerPpiGuid = {0xbec47213, 0x39da, 0x4352, {0x9f, 0x12, 0xa1, 0x37, 0x8f, 0xb3, 0xfb, 0x44}};

EFI_STATUS
SetCmosOnEndofPei(
  IN EFI_PEI_SERVICES           **PeiServices,
  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
  IN VOID                       *InvokePpi
);

EFI_STATUS
CmosManagerInstallNotify(
  IN EFI_PEI_SERVICES           **PeiServices,
  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
  IN VOID                       *InvokePpi
);

static EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList[] = {
    { EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK,
      &gEfiEndOfPeiSignalPpiGuid,
      SetCmosOnEndofPei
    },
    { EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
      &gEfiCmosManagerPpiGuid,
      CmosManagerInstallNotify
    }
};

EFI_STATUS
SetCmosOnEndofPei (
  IN EFI_PEI_SERVICES           **PeiServices,
  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
  IN VOID                       *InvokePpi
)
{
    IoWrite8(0x70, 0xF1);
    IoWrite8(0x71, 0xAA);

    return EFI_SUCCESS;
}

/***************************************************************************
*                        Example 3, Install a PEIM                         *
****************************************************************************/
typedef struct _EFI_PEI_CMOS_MANAGER_PPI EFI_PEI_CMOS_MANAGER_PPI;

typedef
EFI_STATUS
(EFIAPI *EFI_PEI_CMOS_READ)(
  IN  UINTN        Index,
  OUT UINTN       *Value
);

typedef
EFI_STATUS
(EFIAPI *EFI_PEI_CMOS_WRITE)(
  IN UINTN        Index,
  IN UINTN        Value
);

struct _EFI_PEI_CMOS_MANAGER_PPI
{
  EFI_PEI_CMOS_READ  PeiCmosRead;
  EFI_PEI_CMOS_WRITE PeiCmosWrite;
};

EFI_STATUS
EFIAPI
CmosManagerRead(
  IN  UINTN       Index,
  OUT UINTN      *Value
);

EFI_STATUS
EFIAPI
CmosManagerWrite(
  IN UINTN        Index,
  IN UINTN        Value
);


EFI_PEI_CMOS_MANAGER_PPI mCmosManagerPpi = {
  CmosManagerRead,
  CmosManagerWrite
};

EFI_PEI_PPI_DESCRIPTOR mCmosManagerPpiList = {
  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
  &gEfiCmosManagerPpiGuid,
  &mCmosManagerPpi
};

EFI_STATUS
EFIAPI
CmosManagerRead(
  IN  UINTN        Index,
  OUT UINTN       *Value
){
  IoWrite8(0x70, (UINT8)Index);
  *Value = IoRead8(0x71);

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
CmosManagerWrite(
  IN  UINTN         Index,
  IN  UINTN         Value
){
    IoWrite8(0x70, (UINT8)Index);
    IoWrite8(0x71, (UINT8)Value);

    return EFI_SUCCESS;
}

EFI_STATUS
CmosManagerInstallNotify(
  IN EFI_PEI_SERVICES           **PeiServices,
  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
  IN VOID                       *InvokePpi
)
{
    EFI_STATUS Status;
    EFI_PEI_CMOS_MANAGER_PPI *CmosManagerPpi;

    Status = PeiServicesLocatePpi (&gEfiCmosManagerPpiGuid, 0, NULL, (VOID **) &CmosManagerPpi);
    if (!EFI_ERROR (Status)) {
        CmosManagerPpi->PeiCmosWrite(0xFD, 0xAA);
    }

    return EFI_SUCCESS;
}
// Example 3 end


EFI_STATUS
CmosManagerPeiEntryPoint (
    IN EFI_PEI_FILE_HANDLE         FileHandle,
    IN CONST EFI_PEI_SERVICES   ** PeiServices 
)
{
    EFI_STATUS    Status;

    Status = (*PeiServices)->NotifyPpi (PeiServices, mNotifyList);
    if (EFI_ERROR (Status)) {
        return Status;
    }  

    Status = (*PeiServices)->InstallPpi (PeiServices, &mCmosManagerPpiList);
    if (EFI_ERROR (Status)) {
        return Status;
    }  

    return EFI_SUCCESS;
}
